/**
**************************************************************************************
*Palisma - Secrets of the Illuminati is an open-source 2D RPG                        *
*Copyright (C) 2006, Tony Sparks                                                     *
*                                                                                    *
*This library is free software; you can redistribute it and/or                       *
*modify it under the terms of the GNU Lesser General Public                          *
*License as published by the Free Software Foundation; either                        *
*version 2.1 of the License, or (at your option) any later version.                  *
*                                                                                    *
*This library is distributed in the hope that it will be useful,                     *
*but WITHOUT ANY WARRANTY; without even the implied warranty of                      *
*MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU                   *
*Lesser General Public License for more details.                                     *
*                                                                                    *
*You should have received a copy of the GNU Lesser General Public                    *
*License along with this library; if not, write to the Free Software                 *
*Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA      *
**************************************************************************************
*/
#include "StdAfx.h"
#include "Kernel.h"
#include <iostream>
#include <stdio.h>
#include <time.h>
 

Kernel::Kernel(void)
{
    m_delta = 0;
    m_lastFrame = 0;
    timer.Refresh();
}
/** Init the kernel */
int Kernel::Init()
{
    // Init SDL
    if(SDL_Init(SDL_INIT_VIDEO|SDL_INIT_TIMER) < 0){ 
        std::cerr << "Couldn't init SDL"; 
        return 1;
    }
    //Now that we're enabled, make sure we cleanup
    atexit(SDL_Quit); 
    return 0;
}
 
/** Shutdown all Processes */
void Kernel::Shutdown()
{
    // Kill all the processes
    KillAll();

    // close sdl
    SDL_Quit();
}

/** Run the kernel */
void Kernel::Execute()
{   
    m_lastFrame = GetTime();
    const int MAX_FRAMESKIP = 5;
    float interp = 0.0f;
    int loops;
    long  nextTick = GetTime();

    // Execute all processes in order
    // of priority
    while(proList.size())
    {
        timer.Refresh();
        const int SKIP_TICKS = 1000 / m_cvars->GetFloatValue("v_maxfps");


        // calculate how long it was since we last came round this loop
        // and hold on to it so we can let the updating/rendering routine
        // know how much time to update by
        m_delta = (long)(GetTime() - m_lastFrame);
        m_lastFrame = GetTime();

		std::list< CMMPointer<IProcess> >::iterator it;
		for(it=proList.begin(); it!=proList.end(); ++it)
		{
			IProcess* t = (*it);
			
            if(!t->CanKill() && t->IsActive() ) {
                if ( dynamic_cast< GameManager* >(t) ) {
                    loops = 0;
                    
                    while( GetTime() > nextTick && loops < MAX_FRAMESKIP ) {
                        t->Update( (SKIP_TICKS*(loops+1))-GetFrameTime() );
                        nextTick += SKIP_TICKS;
                        loops++;
                        
                    }

                }
                else {
                    t->Update( GetFrameTime() );
                }
            }
		}

		// loop again to remove dead tasks
		it = proList.begin();
        while( it!=proList.end() )
		{
			IProcess* t = (*it);
			
			if(t->CanKill())
			{
                t->Shutdown();
				it = proList.erase( it );
				t = 0;
			}
            else
            {
                ++it;
            }
		}
        IMMObject::CollectGarbage();
    }
}

/**
=======================
Get the current time 
=======================
*/
float Kernel::GetTime() 
{
    return (float)SDL_GetTicks();
}

/**
========================
Get the FrameTime - frametime is one game loop
========================
*/
float Kernel::GetFrameTime()
{
    return timer.GetFrameTime()*1000.0f;
}

/** Print to the console */
void Kernel::Printf( const char *s, ... )
{
    char buffer[1024]; 
	va_list arguments;
    // parse out the arguments, and put them in 
    // string format
	va_start(arguments, s);
	      //  _vsnprintf_s(buffer, sizeof(buffer), s, arguments);
	    vsprintf(buffer, s, arguments);
    va_end(arguments);

    m_console->Print( buffer );
}

/** Print to the console and log the contents */
void Kernel::LPrintf( const char *s, ... )
{
    char buffer[1024]; 
	va_list arguments;
    // parse out the arguments, and put them in 
    // string format
	va_start(arguments, s);
	      //  _vsnprintf_s(buffer, sizeof(buffer), s, arguments);
	    vsprintf(buffer, s, arguments);
    va_end(arguments);

    bool status = m_console->IsLogging();
    m_console->SetLog( true );
    m_console->Print( buffer );  
    m_console->SetLog( status );
}


void Kernel::AddProcess(CMMPointer<IProcess> &p)
{
	// keep the order of priorities straight
	std::list< CMMPointer<IProcess> >::iterator it;
	for(it=proList.begin();it!=proList.end();it++)
	{
		CMMPointer<IProcess> &comp=(*it);
		// if this priority is greater,
        // we want to insert it now
        if(comp->GetPriority() > p->GetPriority() )
            break;
	}
    // insert the process
	proList.insert(it,p);
}


void Kernel::SuspendProcess(CMMPointer<IProcess> &p)
{
	//check that this task is in our list - we don't want to suspend a task that isn't running
	if(std::find( proList.begin(), proList.end(), p) != proList.end())
	{
		proList.remove(p);
		pausedList.push_back(p);
	}
}


void Kernel::ResumeProcess(CMMPointer<IProcess> &p)
{
    if(std::find( pausedList.begin(), pausedList.end(), p ) != pausedList.end() )
    {
        pausedList.remove(p);
        // insert in the proper order
        std::list< CMMPointer<IProcess> >::iterator it;
        for ( it = proList.begin(); it != proList.end(); it++ )
        {
            if ( (*it)->GetPriority() > p->GetPriority() )
                break;
        }
        // insert in properly
        proList.insert( it, p );
    }

}
void Kernel::RemoveProcess(CMMPointer<IProcess> &p)
{
    // Kill the process
    if( std::find( proList.begin(), proList.end(), p) != proList.end() )
    {
        p->SetKill( true );
    }
}
void Kernel::KillAll()
{
    std::list< CMMPointer<IProcess> >::iterator it;
    for ( it = proList.begin(); it != proList.end(); it++ )
    {
        (*it)->SetKill( true );
    }
}

Kernel::~Kernel(void)
{
}
